package netengine;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey; 
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.spi.AbstractSelector;
import java.nio.channels.spi.SelectorProvider;
import java.util.ArrayList;

import main.Game;

import org.apache.log4j.Logger;
 
import thread.ThreadManager;

public class NetEngine implements INetEngine 
{
	static Logger logger = Logger.getLogger(Game.class);
	
	public static NetEngine getInstance()
	{
		if (instance == null)
		{
			synchronized(NetEngine.class)
			{
				if (instance == null)
				{
					instance = new NetEngine();
				}
			}
		}
		
		return instance;
	}
	
	private NetEngine()
	{ 
	}
	
	public boolean initialize(int threadCount)
	{
		boolean result = true;
		
		concurrency = 1;
		 
		if (threadCount > 1)
		{
			concurrency = threadCount;
		}
		
		netThreads = new ArrayList<NetThread>(concurrency);
		
		try
		{
			for (int i = 0; i < concurrency; i++)
			{
				SelectorProvider systemDefaultProvider = SelectorProvider.provider();
				AbstractSelector selector = systemDefaultProvider.openSelector(); 
				NetThread netThread = new NetThread(selector);
			
				netThreads.add(netThread);
				ThreadManager.getInstance().createThread(netThread, false);
			}
			
			logger.info("net engine intialize ok.");
		}
		catch (IOException ioexception)
		{
			result = false;
			logger.error("Exception: ", ioexception);
		}
		
		return result;
	}
	 
	public SelectionKey registerHandler(SelectableChannel channel, int ops, Object attachment)
	{
		int netThreadCount = netThreads.size();
		int hashCode       = attachment.hashCode();
		int index          = hashCode % netThreadCount; 
		
		NetThread thread = netThreads.get(index);
		SelectionKey key = thread.register(channel, ops, attachment);
		
		return key;
	}
	 
	public boolean openListenPort(int port)
	{
		boolean result = true;
		
		try
		{ 
			SelectorProvider systemDefaultProvider = SelectorProvider.provider();
			ServerSocketChannel channel = systemDefaultProvider.openServerSocketChannel(); 
			ServerSocket serverSocket = channel.socket();
			serverSocket.bind(new InetSocketAddress(port));
			channel.configureBlocking(false);
			
			NetAcceptHandler acceptHandler = new NetAcceptHandler(channel);
			SelectionKey key = registerHandler(channel, SelectionKey.OP_ACCEPT, acceptHandler);
			acceptHandler.setSelctionKey(key);
			 
			logger.info("open listen on port " + port + ".");
		}
		catch(IOException exception)
		{
			logger.error("Exception: ", exception);
		}
		
		return result;
	}
	
	public boolean closeListenPort()
	{
		return true;
	}
	
	private int 				 concurrency;
	private ArrayList<NetThread> netThreads;
	private static NetEngine 	 instance;
}
